package Init

import (
	"context"
	"fmt"
	"strings"

	dlog "gin-gateway/core/log"
	"github.com/gin-gonic/gin"
)

// 通用DLTag常量定义
const (
	DLTagUndefind      = "_undef"
	DLTagMySqlFailed   = "_com_mysql_failure"
	DLTagRedisFailed   = "_com_redis_failure"
	DLTagMySqlSuccess  = "_com_mysql_success"
	DLTagMySqlSlow     = "_com_mysql_slow"
	DLTagMySql         = "_com_mysql"
	DLTagRedisSuccess  = "_com_redis_success"
	DLTagThriftFailed  = "_com_thrift_failure"
	DLTagThriftSuccess = "_com_thrift_success"
	DLTagHTTPSuccess   = "_com_http_success"
	DLTagHTTPFailed    = "_com_http_failure"
	DLTagTCPFailed     = "_com_tcp_failure"
	DLTagRequestIn     = "_com_request_in"
	DLTagRequestOut    = "_com_request_out"

	GinTraceKey = "_trace"
)

const (
	_dlTag          = "dltag"
	_traceId        = "traceid"
	_spanId         = "spanid"
	_childSpanId    = "cspanid"
	_dlTagBizPrefix = "_com_"
	_dlTagBizUndef  = "_com_undef"
)

var Log *Logger

type Trace struct {
	TraceId     string
	SpanId      string
	Caller      string
	SrcMethod   string
	HintCode    int64
	HintContent string
}

type TraceContext struct {
	Trace
	CSpanId string
}

type TraceKey struct{}

func TraceFromContext(c context.Context) *TraceContext {
	if c == nil {
		return NewTrace()
	}
	traceContext := c.Value(TraceKey{})
	if tc, ok := traceContext.(*TraceContext); ok {
		return tc
	}
	return NewTrace()
}

func NewTraceContext(c *gin.Context) context.Context {
	return context.WithValue(context.Background(), TraceKey{}, TraceFromGinContext(c))
}

func TraceFromGinContext(c *gin.Context) *TraceContext {
	if c == nil {
		return NewTrace()
	}
	traceContext, exists := c.Get(GinTraceKey)
	if exists {
		if tc, ok := traceContext.(*TraceContext); ok {
			return tc
		}
	}
	return NewTrace()
}

type Logger struct {
}

func (l *Logger) TagInfo(trace *TraceContext, dltag string, m map[string]interface{}) {
	dlog.Info(paramsEncode(trace, dltag, m))
}

func (l *Logger) TagWarn(trace *TraceContext, dltag string, m map[string]interface{}) {
	dlog.Warn(paramsEncode(trace, dltag, m))
}

func (l *Logger) TagError(trace *TraceContext, dltag string, m map[string]interface{}) {
	dlog.Error(paramsEncode(trace, dltag, m))
}

func (l *Logger) TagTrace(trace *TraceContext, dltag string, m map[string]interface{}) {
	dlog.Trace(paramsEncode(trace, dltag, m))
}

func (l *Logger) TagDebug(trace *TraceContext, dltag string, m map[string]interface{}) {
	dlog.Debug(paramsEncode(trace, dltag, m))
}

func (l *Logger) Close() {
	dlog.Close()
}

// 生成业务dltag
func CreateBizDLTag(tagName string) string {
	if tagName == "" {
		return _dlTagBizUndef
	}

	return _dlTagBizPrefix + tagName
}

func paramsEncode(trace *TraceContext, dltag string, m map[string]interface{}) string {
	var s strings.Builder
	s.WriteString(dltag)
	s.WriteString(";")

	s.WriteString(_traceId)
	s.WriteString("=")
	s.WriteString(trace.TraceId)
	s.WriteString(";")
	delete(m, _traceId)
	s.WriteString(_spanId)
	s.WriteString("=")
	s.WriteString(trace.SpanId)
	s.WriteString(";")
	delete(m, _spanId)
	s.WriteString(_childSpanId)
	s.WriteString("=")
	s.WriteString(trace.CSpanId)
	s.WriteString(";")
	delete(m, _childSpanId)
	if uri, ok := m["uri"]; ok {
		s.WriteString("uri")
		s.WriteString("=")
		s.WriteString(uri.(string))
		s.WriteString(";")
		delete(m, "uri")
	}

	for key, val := range m {
		// if key == dltag || key == _traceId || key == _spanId || key == _childSpanId {
		// 	continue
		// }
		s.WriteString(key)
		s.WriteString("=")
		s.WriteString(fmt.Sprintf("%+v", val))
		s.WriteString(";")
	}
	return s.String()
}
